package org.truenewx.tnxjee.repo.redis.concurrent;

import java.util.*;

import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.truenewx.tnxjee.core.util.LogUtil;
import org.truenewx.tnxjee.core.util.concurrent.ProgressTask;
import org.truenewx.tnxjee.core.util.concurrent.ProgressTaskExecutor;
import org.truenewx.tnxjee.core.util.concurrent.TaskProgress;
import org.truenewx.tnxjee.repo.redis.support.RedisLockKeyPrefixSupplier;

/**
 * 基于Redis同步锁的进度任务执行器，实现复制自{@link ProgressTaskExecutor}
 *
 * @param <S> 任务执行器键类型
 * @param <P> 进度类型
 */
@EnableScheduling
public abstract class RedisLockProgressTaskExecutor<S, P extends TaskProgress<String>>
        extends RedisLockTaskExecutor<S> {

    private final Map<String, P> progresses = new Hashtable<>();

    public RedisLockProgressTaskExecutor(ApplicationContext applicationContext,
            RedisLockKeyPrefixSupplier redisPrefixSupplier) {
        super(applicationContext, redisPrefixSupplier);
    }

    public String submit(S key, long expiredMilliseconds, boolean throwOnRunning, ProgressTask<P> task) {
        super.submit(key, expiredMilliseconds, throwOnRunning, task);
        // 任务提交后再缓存进度，以免任务提交失败还缓存进度
        P progress = task.getProgress();
        String progressId = progress.getId();
        this.progresses.put(progressId, progress);
        return progressId;
    }

    public String submit(S key, boolean throwOnRunning, ProgressTask<P> task) {
        return submit(key, 0, throwOnRunning, task);
    }

    public boolean isProgressing(String progressId) {
        P progress = getProgress(progressId);
        return progress != null && progress.isRunning();
    }

    public Collection<P> getProgresses() {
        return this.progresses.values();
    }

    public P getProgress(String progressId) {
        P progress = this.progresses.get(progressId);
        if (progress != null && progress.isCleanable()) {
            progress = null;
            remove(progressId);
        }
        return progress;
    }

    public void remove(String progressId) {
        P progress = this.progresses.remove(progressId);
        if (progress != null) {
            progress.end();
            LogUtil.info(getClass(), "====== The completed task progress(id={}) has been ended and removed.",
                    progressId);
        }
    }

    /**
     * 清理掉所有已完成的任务进度
     */
    @Scheduled(cron = "0 0/10 * * * ?") // 每10分钟执行一次
    public void clean() {
        Set<String> progressIds = new LinkedHashSet<>();
        for (P progress : this.progresses.values()) {
            if (progress.isCleanable()) {
                progressIds.add(progress.getId());
            }
        }
        for (String progressId : progressIds) {
            remove(progressId);
        }
    }

}
